import pyglet
from pyglet.window import key
import gg

# in current pyglet trunk joystick support is experimental
# so sometimes totally ignoring can be handy
if gg.force_no_joystick:
    mod_joystick = None
else:
    import joystick as mod_joystick


class AppModel(pyglet.event.EventDispatcher):
    def __init__(self):
        self.st_name = None
        self.in_game = False
        self.set_defaults_controller()

    def start(self):
        self.go_state('main_menu')

    def go_state(self, st_name):
        self.st_name = st_name
        getattr(self, 'enter_' + st_name)()
        
    def enter_main_menu(self):
        options=[
            "load_game",
            "new_game",
            "player_settings",
            "general_settings",
            "high_scores",
            "quit"
             ]
        ev_data = {
            'dialog_name':'main_menu',
            'options': options,
            'cbak_selection_done': self.on_main_menu_selection_done
            }
        self.dispatch_event('on_show_dialog', ev_data)

    def on_main_menu_selection_done(self, choice):
        self.dispatch_event('on_destroy_dialog', 'main_menu')
        self.go_state(choice)

    def enter_quit(self):
        self.dispatch_event('on_terminate_app')

    def enter_new_game(self):
        gg.stage = 0
        params_for_options_list = [
            ('sp', None),
            ('sp_sp',( 'sp', 'player1', None)),
            ('coop', None),
            ('coop_sp', ('coop', 'player1', 'IAcoop')),
            ('coop_2p', ('coop', 'player1', 'player2')),
            ('sxs', None),
            ('sxs_sp', ( 'sxs', 'player1', 'IAsxs')),
            ('sxs_2p', ( 'sxs', 'player1', 'player2')),
            ('steal', None),
            ('steal_sp', ('steal','player1', 'IAsteal')),
            ('steal_2p', ('steal','player1', 'player2'))
            ]
        self.game_modes_options = {}
        self.game_modes_options.update(params_for_options_list)             
        options = [ k for k,v in params_for_options_list]
        if not hasattr(self, 'game_mode_choice'):
            self.game_mode_choice = 'sp_sp'
        if not hasattr(self, 'ord_mutators_flags'):
            self.ord_mutators_flags = [
                ('swap_teams', False),
                ('pigeons_nowayback', False),
                ('birds_nowayback', False)
                ]
        ev_data = {
            'dialog_name': 'game_mode',
            'options': options,
            'starting_choice': self.game_mode_choice,
            'ord_mutators_flags': self.ord_mutators_flags,
            'cbak_selection_done': self.game_mode_selection_done,
            'cbak_back': self.game_mode_on_back
            }
            
        self.dispatch_event('on_show_dialog', ev_data )

    def game_mode_selection_done(self, choice, res_ord_mutators_flags):
        if self.game_modes_options[choice] is None:
            return
        print '\sel done - choice, mut flags:',choice, res_ord_mutators_flags
        self.dispatch_event('on_destroy_dialog', 'game_mode')
        self.game_mode_choice = choice
        self.ord_mutators_flags = res_ord_mutators_flags
        gm, mind1, mind2 = self.game_modes_options[choice]
        game_mode = {}        
        game_mode['game_mode'] = gm
        for m, v in self.ord_mutators_flags:
            game_mode[m] = v
        game_mode['players'] = [mind1, mind2]
        self.game_mode = game_mode
        self.go_state('play')

    def game_mode_on_back(self):
        self.dispatch_event('on_destroy_dialog', 'game_mode')
        self.go_state('main_menu')

    def set_defaults_controller(self):
        # writing bindings in the direction key to command allows for easy
        # alternative bindings, ie two keys for one command
        self.kbd_player_bindings_defaults = { # variant_id: bindings dict
            'arrows': { # key: command
                key.LEFT: 'left',
                key.RIGHT: 'right',
                key.UP: 'up',
                key.DOWN: 'down',
                key.RCTRL: 'quack_unfollow'
                },
        
            'wasd':{
                key.A: 'left',
                key.D: 'right',
                key.W: 'up',
                key.S: 'down',
                key.LSHIFT: 'quack_unfollow'
                },

            'ijkl':{
                key.J: 'left',
                key.L: 'right',
                key.I: 'up',
                key.K: 'down',
                key.SPACE: 'quack_unfollow'
                },

            'numpad':{
                key.NUM_4: 'left',
                key.NUM_6: 'right',
                key.NUM_8: 'up',
                key.NUM_5: 'down',
                key.NUM_0: 'quack_unfollow'
                }
            }
        self.controller_choices = ['kbd_arrows', 'kbd_wasd']

    def enter_player_settings(self):
        kbd_names = [ ('kbd_'+k) for k in self.kbd_player_bindings_defaults ]
        self.available_joysticks, joy_names = self.build_joysticks_info()
        controller_names = kbd_names + joy_names
        ev_data = {
            'dialog_name': 'player_settings',
            'options': controller_names,
            'starting_choices': self.controller_choices,
            'cbak_done': self.player_settings_on_done,
            'cbak_escape': self.player_settings_escape,
            }
        self.dispatch_event('on_show_dialog', ev_data )

    def sanitize_player_settings(self):
        # silently replaces invalid joy choices
        available_joysticks, joy_names = self.build_joysticks_info()
        for device_num, choice in enumerate(list(self.controller_choices)):
            if choice.startswith('joy'):
                #? weak validation: only checks the joynum exists, but doesnt
                # catch reorders or substitution, maybe with incompatible
                # device
                joy_num = int(choice[-1])
                if joy_num >= len(available_joysticks):
                    # reasign choice to first available kbd
                    other_choice = self.controller_choices[1-device_num]
                    for e in [ ('kbd_'+k) for k in self.kbd_player_bindings_defaults]:
                        if e != other_choice:
                            break
                    self.controller_choices[device_num] = e

    def player_settings_escape(self,*args):
        self.dispatch_event('on_destroy_dialog', 'player_settings')
        self.go_state('main_menu')        

    def player_settings_on_done(self, controller_choices):
        self.controller_choices = controller_choices
        self.dispatch_event('on_destroy_dialog', 'player_settings')
        self.go_state('main_menu')

    def build_joysticks_info(self):
        if mod_joystick is None:
            # in current pyglet trunk joystick support is experimental
            # so sometimes totally ignoring can be handy
            available_joysticks = []
        else:
            available_joysticks = mod_joystick.enumerate_joysticks()
        joy_names = [ 'joy_%1d'%i for i in xrange(len(available_joysticks))]
        return available_joysticks, joy_names
            

    def enter_general_settings(self):
        # not implemented
        self.go_state('main_menu')

    def enter_high_scores(self):
        # not implemented
        self.go_state('main_menu')

    def enter_play(self):
        self.sanitize_player_settings()
        controller_creation_info = self.prepare_controller_creation_info()
        world_creation_info = self.prepare_world_creation_info()
        self.dispatch_event('on_play_stage',
                            controller_creation_info, world_creation_info)
        print '\nafter dispatching on_play_stage'
        #self.next_scene
        
    def controller_add_player_bindings(self, kbd_bindings, j_to_clear, 
                                       joy_bindings, ctroller_num):
        # add player 1 bindings
        dest = 'ctroller%1d'%ctroller_num
        device, variant = self.controller_choices[ctroller_num].split('_')
        if device == 'kbd':
            binds = self.kbd_player_bindings_defaults[variant]
            for k in binds:
                kbd_bindings[k] = (dest, binds[k])
        else:
            # joystick
            # atm variant only gives the joy_num in the available_joysticks
            j_binds , j_to_clear1 = mod_joystick.make_bind_data_PS2base(dest)
            j_to_clear.extend(j_to_clear1)
            joy_bindings.append((self.available_joysticks[int(variant)], j_binds))

    def prepare_controller_creation_info(self):
        joy_bindings = []
        j_to_clear = []
        # prepare the binding info for world controller
        world_kbd_bindings = {
            key.RETURN:('world','end_help'),
            key.H:('world','begin_help'),
            key.F1:('world','begin_help'),
            key.R:('world','restart'), #restart level
            key.N:('world','warp') # warp to next level
            }
        kbd_bindings = dict(world_kbd_bindings)
        
        for ctroller_num in [0,1]:
            self.controller_add_player_bindings(kbd_bindings, j_to_clear,
                                    joy_bindings, ctroller_num)
        #? build inconditionally even if second player not needed ?

##        # if necesary, add player 2 bindings
##        if self.game_mode['players'][1].startswith('player'):
##            # same as before changing dest
##            dest = 'ctroller2'
##            device, variant = self.controller_choices[1].split('_')
##            if device == 'kbd':
##                binds = self.kbd_player_bindings_defaults[variant]
##                for k in binds:
##                    kbd_bindings[k] = (dest, binds[k])
##            else:
##                # joystick
##                # atm variant only gives the joy_num in the available_joysticks
##                j_binds , j_to_clear1 = mod_joystick.make_bind_data_PS2base(dest)
##                j_to_clear.extend(j_to_clear1)
##                joy_bindings.append((variant, j_binds))
        info = {
            'kbd_bindings': kbd_bindings,
            'joy_bindings': joy_bindings,
            'joy_to_clear': j_to_clear,
            'enable_coop_sp': (self.game_mode_choice == 'coop_sp')
            }
        return info
        
    def prepare_world_creation_info(self):
        def get_player_info(mind):
            if (mind is not None) and mind.startswith('player'):
                ctrl_num = int(mind[-1])-1
                button_group = 'ctroller%1d'%ctrl_num
                mind = 'human'
            else:
                button_group = 'orders'
            info = {
                'mind_type': mind,
                'button_group': button_group
                }
            #note: HUD updates will also use the button_grp as identifier, so
            # no need for a separate identifier.
            return info

        game_mode = self.game_mode
        players_info = []
        for player in game_mode['players']:
            players_info.append(get_player_info(player))
        if game_mode['swap_teams']:
            players_info.reverse()

        info = {
            'players': players_info,
            'game_mode': game_mode['game_mode'],
            'pigeons_nowayback': game_mode['pigeons_nowayback'],
            'birds_nowayback': game_mode['birds_nowayback'],
            'stage': gg.stage
            }
        return info

    def next_level(self):
        gg.stage += 1
        self.enter_play()
        
#? si los stats son acumulativos deberiamos pasarlos aqui tambien, pero eso lo
# dejamos para un refactor posterior

AppModel.register_event_type('on_show_dialog')
AppModel.register_event_type('on_destroy_dialog')
AppModel.register_event_type('on_terminate_app')
AppModel.register_event_type('on_play_stage')
##AppModel.register_event_type()
##AppModel.register_event_type()
##AppModel.register_event_type()

